#ifndef _VERTEXARRAYLOADER_H_
#define _VERTEXARRAYLOADER_H_

#include <SERender/Util/Util.h>
#include <array>
#include <vector>
#include <memory>
#include <SERender/Resourses/VertexArray.h>
#include <cmath>
#include <glm/glm.hpp>
#include <SERender/Drive/Drive.h>

namespace SEngine
{
    template<size_t... Layout>
    class VertexArrayLoader 
    {
    public:
        using Vertex = std::array<float, AllSize<Layout...>()>;
        using Face = std::array<uint32_t, 3>;
    public:
        VertexArrayLoader(std::unique_ptr<IDrive> && drive) : m_drive(std::move(drive)) {}
        virtual ~VertexArrayLoader() = default;

        VertexArrayLoader<Layout...> & PushVertex(Vertex && vertex);
        VertexArrayLoader<Layout...> & PushFace(Face && face);

        std::unique_ptr<VertexArray> CreateVertexArray();

    private:
        template<size_t T, size_t... Ts>
        void SetPointAttribute(int slot = 0, size_t offset = 0);

    private:
        std::vector<Vertex> m_vertexs;
        std::vector<Face> m_faces;
        std::unique_ptr<IDrive> m_drive;
    };
    
    template <size_t... Layout>
    inline VertexArrayLoader<Layout...> & VertexArrayLoader<Layout...>::PushVertex(Vertex && vertex)
    {
        m_vertexs.emplace_back(vertex);
        return *this;
    }

    template <size_t... Layout>
    inline VertexArrayLoader<Layout...> & VertexArrayLoader<Layout...>::PushFace(Face && face)
    {
        m_faces.emplace_back(face);
        return *this;
    }

    template <size_t... Layout>
    inline std::unique_ptr<VertexArray> VertexArrayLoader<Layout...>::CreateVertexArray()
    {
        auto vao = std::make_unique<VertexArray>(glm::max(m_vertexs.size(), m_vertexs.size() * 3));
        vao->Bind();
        m_drive->BufferData(ARRAY_BUFFER, m_vertexs.size() * AllSize<Layout...>() * sizeof(float), m_vertexs.data()->data(), DYNAMIC_DRAW);
        m_drive->BufferData(ELEMENT_ARRAY_BUFFER, m_faces.size() * 3 * sizeof(uint32_t), m_faces.data()->data(), DYNAMIC_DRAW);
        SetPointAttribute<Layout...>();
        vao->Unbind();
        return vao;
    }

    template <size_t... Layout>
    template <size_t T, size_t... Ts>
    inline void VertexArrayLoader<Layout...>::SetPointAttribute(int slot, size_t offset)
    {
        m_drive->VertexAttribPointer(slot, T, FLOAT, false, (int)AllSize<Layout...>() * sizeof(float), (void*)offset);
        m_drive->EnableVertexAttribArray(slot);
        if constexpr (sizeof...(Ts) != 0) {
            SetPointAttribute<Ts...>(slot + 1, offset + T * sizeof(float));
        }
    }

} // namespace SEngine

#endif